﻿using System;
using Microsoft.Practices.ObjectBuilder2;
using Microsoft.Practices.Unity;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using Rhino.Mocks;
using TestByWire.BuildStrategies;
using TestByWire.Factories;
using TestByWire.Factories.StubbingStrategies;
using TestByWire.Test.Fakes;

namespace TestByWire.Test.BuildStrategies
{
    [TestClass]
    public class AutoInjectionBuildStrategyTest
    {
        private IUnityContainer _container;
        private AutoInjectionBuildStrategy _autoInjectionBuildStrategy;
        private StubInstanceFactory _stubInstanceFactory;
        private Type _typeUnderTest;

        [TestInitialize]
        public void PerTestSetup()
        {
            _container = new UnityContainer();
            _typeUnderTest = this.GetType();
            _stubInstanceFactory = new StubInstanceFactory(new StubCreationStrategyFactory(_container), _container);
            _autoInjectionBuildStrategy = new AutoInjectionBuildStrategy(_container, _stubInstanceFactory, _typeUnderTest);
            new AutoInjectionBehavior(_typeUnderTest);
        }

        [TestMethod]
        public void ConstructorMustNotAcceptNullAsContainer()
        {
            var exception = AssertEx.Throws<ArgumentNullException>(() => new AutoInjectionBuildStrategy(null, _stubInstanceFactory, _typeUnderTest));

            Assert.AreEqual("container", exception.ParamName);
        }

        [TestMethod]
        public void ConstructorMustNotAcceptNullAsStubInstanceFactory()
        {
            var exception = AssertEx.Throws<ArgumentNullException>(() => new AutoInjectionBuildStrategy(_container, null, _typeUnderTest));

            Assert.AreEqual("stubInstanceFactory", exception.ParamName);
        }

        [TestMethod]
        public void ConstructorMustNotAcceptNullAsTypeUnderTest()
        {
            var exception = AssertEx.Throws<ArgumentNullException>(() => new AutoInjectionBuildStrategy(_container, _stubInstanceFactory, null));

            Assert.AreEqual("typeUnderTest", exception.ParamName);
        }

        [TestMethod]
        public void PreBuildUpMustDoNothingIfBuildKeyTypeIsTypeUnderTest()
        {
            var builderContext = MockRepository.GenerateStub<IBuilderContext>();
            var buildKey = new NamedTypeBuildKey(_typeUnderTest);
            builderContext.Stub(b => b.OriginalBuildKey).Return(buildKey);
            builderContext.BuildKey = buildKey;

            _autoInjectionBuildStrategy.PreBuildUp(builderContext);

            builderContext.AssertWasNotCalled(c => c.Existing = Arg<object>.Is.Anything);
        }

        [TestMethod]
        public void PreBuildUpMustCreateAStubOfTheProvidedType()
        {
            var builderContext = MockRepository.GenerateStub<IBuilderContext>();
            var buildKey = new NamedTypeBuildKey(typeof(ICalculator));
            builderContext.BuildKey = buildKey;
            builderContext.Stub(b => b.OriginalBuildKey).Return(buildKey);

            _autoInjectionBuildStrategy.PreBuildUp(builderContext);

            Assert.IsNotNull(_container.Resolve<Computer>());
        }

        [TestMethod]
        public void AutoInjectionBuildStrategyMustRegisterStubInstanceOnNameIfProvided()
        {
            const string TheInstanceName = "TheInstanceName";

            var builderContext = MockRepository.GenerateStub<IBuilderContext>();
            var buildKey = new NamedTypeBuildKey(typeof(ICalculator), TheInstanceName);
            builderContext.BuildKey = buildKey;
            builderContext.Stub(b => b.OriginalBuildKey).Return(buildKey);

            _autoInjectionBuildStrategy.PreBuildUp(builderContext);

            var instance = _container.Resolve<ICalculator>(TheInstanceName);
            Assert.IsNotNull(instance);
        }
    }
}
